package com.vmj.heap;

import com.vmj.classfile.entity.constantinfo.ConstantInterfaceMethodRefInfo;

import java.util.List;

/**
 * Created by yangshoujun on 2017/8/26 下午3:12.
 */
public class InterfaceMethodRef extends MemberRef {

    private Method method;

    public static InterfaceMethodRef parseInterfaceMethodRef(RuntimeConstantPool runtimeConstantPool, ConstantInterfaceMethodRefInfo refInfo) {

        InterfaceMethodRef ref = new InterfaceMethodRef();
        ref.setRuntimeConstantPool(runtimeConstantPool);
        ref.setClassName(refInfo.getClassName());
        String[] nameAndType = refInfo.getNameAndDescriptor();
        ref.setName(nameAndType[0]);
        ref.setDescriptor(nameAndType[1]);
        return ref;
    }

    public Method resolvedInterfaceMethod() {
        if(this.method == null) {
            resolvedInterfaceMethodRef();
        }
        return this.method;
    }

    private Method resolvedInterfaceMethodRef() {
        VMClass thisClass = getRuntimeConstantPool().getClazz();
        VMClass otherClass = resolvedClass();
        if(!otherClass.isInterface()) {
            throw new RuntimeException("java.lang.IncompatialClassChangeError");
        }
        Method method = lookupInterfaceMethod(otherClass, this.getName(), this.getDescriptor());
        if(method == null) {
            throw new RuntimeException("java.lang.NoSuchMethodError");
        }
        if(!method.isAccessibleTo(thisClass)) {
            throw new RuntimeException("java.lang.IllegalAccessError");
        }
        return this.method = method;
    }

    public Method lookupInterfaceMethod(VMClass clazz, String name, String descriptor) {
        for(Method method : clazz.getMethods()) {
            if(method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
                return method;
            }
        }
        return lookupMethodInInterfaces(clazz.getInterfaces(), name, descriptor);
    }

    private Method lookupMethodInInterfaces(List<VMClass> ifaces, String name, String descriptor) {
        for(VMClass itf : ifaces) {
            for(Method method : itf.getMethods()) {
                if(method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
                    return method;
                }
            }
            return lookupMethodInInterfaces(itf.getInterfaces(), name, descriptor);
        }
        return null;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }
}
